export type UnionToIntersection<U> = (
  U extends any ? (k: U) => void : never
) extends (k: infer I) => void
  ? I
  : never;

// eslint-disable-next-line unused-imports/no-unused-vars
type CreateEnum<T> = T extends [infer E, infer K, infer L]
  ? UnionToIntersection<{
    [key in E & string]: K;
  }>
  : never;

type StringOrNum<T> = T extends string
  ? string
  : T extends number
    ? number
    : never;
export function createEnum<
  K extends string,
  V extends string | number,
  L,
  T extends [K, V, L]
>(
  args: readonly T[]
): Readonly<
  UnionToIntersection<CreateEnum<T>> & {
    get: (key: StringOrNum<T[1]>) => T[2]
    values: () => StringOrNum<T[1]>[]
    getKey: (value: StringOrNum<T[1]>) => string
    list: {
      value: StringOrNum<T[1]>
      label: T[2]
    }[]
  }
  > {
  const obj = Object.create(null);
  const map = new Map<V, L>();
  const list: {
    value: V
    label: L
  }[] = [];

  for (const [e, k, l] of args) {
    obj[e] = k;
    map.set(k, l);
    list.push({
      value: k,
      label: l
    });
  }
  return Object.freeze({
    ...obj,
    get: (key: V) => {
      return map.get(key);
    },
    getKey: (value: V): string => {
      return Object.keys(obj).find(key => obj[key] === value) as string;
    },
    values: () => {
      return list.map(item => {
        return item.value;
      });
    },
    list
  });
}
